/*
 * PyBoostOptional.h
 *
 *  Created on: Dec 28, 2011
 *      Author: allan
 */

#ifndef PYBOOSTOPTIONAL_H_
#define PYBOOSTOPTIONAL_H_

// Boost includes
#include <boost/optional.hpp>
#include <boost/python.hpp>
using namespace boost::python;

template<typename T>
struct boost_optional_to_python_object
{
	static PyObject* convert(const boost::optional<T>& value)
	{
		return (value ? to_python_value<T>()(*value) :
		                boost::python::detail::none());
	}
};

template<typename T>
struct boost_optional_from_python_object
{
	boost_optional_from_python_object()
	{
		converter::registry::push_back(
			&convertible, &construct, type_id<boost::optional<T>>());
	}
	
	static void* convertible(PyObject* obj_ptr)
	{
		using namespace boost::python::converter;

		if(obj_ptr == Py_None)
			return obj_ptr;

		const registration& converters(registered<T>::converters);

		if(implicit_rvalue_convertible_from_python(obj_ptr, converters))
		{
			rvalue_from_python_stage1_data data = 
				rvalue_from_python_stage1(obj_ptr, converters);
			
			return rvalue_from_python_stage2(obj_ptr, data, converters);
		}
		
		return NULL;
	}
	
	static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
	{
		using namespace boost::python::converter;

		void* const storage = ((rvalue_from_python_storage<T>*)data)->storage.bytes;

		if(data->convertible == obj_ptr)	// == None
			new (storage) boost::optional<T>(); // A Boost uninitialized value
		else
			new (storage) boost::optional<T>(*static_cast<T *>(data->convertible));

		data->convertible = storage;
	}
};

template<typename T>
void init_converter_boost_optional_tofrom_python_object()
{
	to_python_converter<boost::optional<T>, boost_optional_to_python_object<T>>();
	boost_optional_from_python_object<T>();
}

#endif /* PYBOOSTOPTIONAL_H_ */
